
package Sgdb::RemoteGui ;
use Sgdb::Utils qw ( :DEFAULT
                     recursive_mkdir
                     recursive_clean_dir
                     ) ;

use warnings ;
use strict ;
use integer ;
use bytes ;

use Net::FTP ;
use Net::Telnet ;


my $login         = pref 'last-rgui-login-name'  ;
my $host          = pref 'last-rgui-host-name'   ;
my $local_scratch = pref 'last-rgui-local-scratch-dir' ;
my $remote_scratch= pref 'last-rgui-remote-scratch-dir' ;
my ($telnet, $ftp, $passwd ) ;
my $PROMPT =  'SgDbFUnnYPrOmpT' ;

init() ;

sub remote_scratch_dir { $remote_scratch } # hell, just publish it

sub init {

    hook_add 'everything-up-and-running', \&init_host_params ;
    $local_scratch =~ s/~/$ENV{HOME}/ ;
    recursive_mkdir     $local_scratch ;
    recursive_clean_dir $local_scratch ;
    die "can't create $local_scratch: $!\n" unless -d $local_scratch ;
    1 ;
}

sub init_host_params {

    # this one called after main window created and before tk mainloop

    my ($ok, $cancel) = ("OK     ", "Cancel " ) ;

    my $d = $mainwindow -> DialogBox ( -title => 'SGDB REMOTE GUI: Get Host' ,
                                       -buttons => [ $ok, $cancel ]
                                       ) ;

    my $errormsg = '' ;
    my @pref_entry = pref 'service-pane-entry' ;
    my @pref_frame = pref 'service-pane-frame' ;

    foreach my $item ([ 'Label',
                        -textvariable => \$errormsg,
                        @pref_frame,
                        -foreground => 'red' ],
                      [ 'Label',
                        -text => 'Host',
                        @pref_frame ],
                      [ 'Entry',
                        -textvariable => \$host ,
                        @pref_entry ],
                      [ 'Label',
                        -text => 'Login' ,
                        @pref_frame ],
                      [ 'Entry',
                        -textvariable => \$login,
                        @pref_entry ],
                      [ 'Label',
                        -text => 'Password',
                        @pref_frame ],
                      [ 'Entry',
                        -textvariable => \$passwd,
                        -show => '*',
                        @pref_entry ],
                     ) { $d -> add ( @$item ) -> pack ( -side => 'top',
                                                        -fill => 'x',
                                                        -padx => 2,
                                                        -pady => 2,
                                                        -anchor => 'n',
                                                      ) }
    while ( 1 ) {
        # now let's try to connect ftp and telnet
        my $show = $d -> Show () ;
        if ($show eq $cancel) {
            die "sgdb remote-gui: operation canceled \n" ;
        }
        $remotegui = "$login\@$host" ;

        telthem "try ftp connection: $remotegui" ;
        unless ( recover_ftp() ) {
            telthem $errormsg = "Cant connect $remotegui" ;
            next ;
        }

        telthem "try telnet connection: $remotegui" ;
        unless ( recover_telnet() ) {
            telthem $errormsg = "Cant connect $remotegui" ;
            next ;
        }

        pref_set 'last-rgui-login-name' => $login ;
        pref_set 'last-rgui-host-name'  => $host  ;

        last ;
    }

    $remotegui = bless \$remotegui, 'Sgdb::RemoteGui' ;

    # now let the world know about it 
    Sgdb::LocalConfig::remotegui_mode ( $remotegui ) ;
}

sub recover_ftp {

    $ftp = new Net::FTP ( $host, -Debug => 0 ) or return ;
    $ftp -> login ( $login, $passwd ) or return ;
    1 ;
}

sub recover_telnet {

    $telnet = new Net::Telnet ( -timeout => 10,
                                -prompt  => '/\\Z/' ,
                                -host    => $host ,
                                -errmode => sub {}, # \&telthemloud,
#                                -telnetmode => 0,
                              ) or return ;

    $telnet -> login( $login, $passwd ) or return ;

    print $telnet "/bin/bash\n" ; # get to know your enemy ..
#    print $telnet "set prompt=$PROMPT\n" ; # csh tcsh
    print $telnet "export PS1=$PROMPT\n" ; # bask ksh zsh

    $telnet -> cmd_remove_mode( 1 ) ;
    $telnet -> prompt ( "/$PROMPT\\s*$/" ) ;
    $telnet -> get ( -timeout => 2 ) ;
    for ( 0 .. 4 ) {
        $telnet -> get ( -timeout => 0.01 ) ;
    }

    1;
}

sub get_stream {

    my $stream  = shift or return ;
    my $timeout = shift || 0.1 ;
    my $size    = shift || 10_000 ;
    my $buf     = '' ;
    my $rmask   = '' ;
    vec($rmask, fileno( $stream ), 1) = 1;

    if (select $rmask, undef, undef, $timeout) {
        sysread $stream, $buf, $size or return ; # undef (eof)
    }
    $buf ;
}

sub telnet {

    my $self = shift or die "Internal: Hey! this is a structured module, do not mess it" ;

    my $cmd  = shift || '' ;
    $cmd =~ s/\s*$/\n/ ;

    my $timeout = shift || 4 ;

    my $buf = get_stream( $telnet, 0.001) ;
    recover_telnet() unless defined $buf ;

    return $buf if $cmd !~ /\S/ ; # let empty command be 'clean buffers'

    telme 'remotegui-leftover: ',$buf if 10 < length $buf and $buf =~ /\S/ ;

    print $telnet $cmd  ;
    telthem "telnet command (T/O: $timeout): $cmd" ;
    $buf = get_stream ($telnet, $timeout ) ;

    unless ( defined $buf ) {

        recover_telnet() or return telthemloud "cannot reocver telnet session" ;
        print $telnet $cmd ;
    }

    $cmd =~ s/\s+$// ;
    my $expr = qr/\Q$cmd\E\s*(.*?)$PROMPT\s*$/s ;

    for ( 0 .. 4 ) {
        if ( $buf =~ $expr ) { $buf = $1 ;  last }
        $buf .= get_stream( $telnet, $timeout ) ;
    }

    $buf=~ y/\r//d ;
    $buf ;
}

sub find_and_get {

    my $self  = shift or die 'hard' ;
    my @files = @_ ;

    for my $foo ( @files ) {

        my ($sanity) = $foo =~ /(\w+)/ ;

        $_ = $remotegui -> telnet ( "ls -d $foo" ) ;

        next if  /No such file or directory/i ;
        next if $sanity and 0 > index $_, $sanity ;

        ( $foo ) = m{^ *(\S.*?)\s*$}m ;
        $foo = $remotegui -> get ( $foo )  ;
        return $foo if -f $foo ;
    }
    return ;                    # undef
}

sub get {
    my $self = shift or die 'hard' ;

    $ftp -> pwd() or recover_ftp() ;

    my $file = shift ;
    my ($f ) = $file =~ m{([^/]*)$} ;

    $ftp -> get( $file, "$local_scratch/$f" ) ;
}

sub init_remote_scratch_directory {

    my $self = shift ;

    my ($home) = $self -> telnet ( 'echo $HOME' ) =~ m{(/\S+)} ;
    $remote_scratch =~ s/~/$home/ ;

    # gross. TODO: more checks
    $self -> telnet ( "rm -Rf $remote_scratch/*" ) ;
    $self -> telnet ( "mkdir -p $remote_scratch" ) ;
    $remote_scratch ;
}

sub session {

    my $self = shift ;
    my $cmd  = shift ;
    $cmd =~ s/\s*$/\n/s ;

    my $session = new Net::Telnet ( -timeout => 10,
                                    -prompt  => '/\\Z/' ,
                                    -host    => $host ,
                                    -errmode => sub {}, # \&telthemloud,
                                    #                                -telnetmode => 0,
                                  ) or return ;

    $session -> login( $login, $passwd ) or return ;

    print $session $cmd ;

    $session ;
}

sub rstub {

    my $self = shift ;
    my $cmd  = join " ", '/emc/jezra/sgdb_gui/rstub.pl', $remote_scratch, @_, 'DonE', '', '' ;
    $self -> telnet( $cmd, 30 ) ;
}
